'''
Author: devis.dong
Email: devis.dong@gmail.com
Date: 2022-10-21 11:48:22
LastEditTime: 2023-01-04 17:45:11
LastEditors: devis.dong
FilePath: \server\server.py
Description:
'''

import socket
import argparse
import threading
from sqltb import *
from myutils import *
from mydefine import *
from fsocket import *
from model_parser import pcd2plist,mesh2pcd
from func_typeNtags import *
from func_index import *
from func_version import *
from func_import import *
from func_SC import *
from func_login import *
from func_download import *
import yaml
from func_internalinterface import *


class ThreadProcess(threading.Thread):
    def __init__(self, connect, fnet, database, database_version):
        threading.Thread.__init__(self)
        self.s = connect
        self.db = database
        self.dbv = database_version
        self.fnet = fnet
    def run(self):


        # 接收模型数据
        head_recved, body_recved = recvModel(self.s)
        if (head_recved is None and body_recved is None):
            self.s.close()
            return
        head_tosend, body_tosend = {"type": RESPONSE_ERROR, "status":'Unkown'}, bytes()
        logger.info("Request Type: %s, Head: %s" % (protocalToStr(head_recved["type"]), head_recved))
        if REQUEST_SEARCHMODEL == head_recved["type"]:
            # 获取模型数据
            block_size = head_recved['block_size']
            modelFilePath = head_recved['info']
            # values = head_recved['values']
            category = getModelCategory(head_recved['category'])
            models = []
            if(0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status":"没有数据！"}
            else:
                with open('config.yml', 'r') as conf_file:
                    ymlConf = yaml.load(conf_file, Loader= yaml.FullLoader)
                    ipfs_url = ymlConf['ipfs']['url']
                # 连接数据库
                tb_model = TBModel(database=self.db, feature_net=self.fnet, ipfs_url=ipfs_url)
                offset = 0  # 数据块的起始地址
                # titles = head_recved['titles']
                # modelFilePath = values[0][titles[2]]
                try:
                    points_list=pcd2plist(mesh2pcd(modelFilePath))
                    points = torch.tensor(list(points_list), dtype=torch.float).view(1, -1, 3)
                    status, models = tb_model.search_by_feature(points, category=category)
                except Exception as e:
                    status = str(e)
                    logger.warning(f'Failed to extract features of "{modelFilePath}", info: {status}')

            # 返回结果
            if(None != status):
                head_tosend = {"type": RESPONSE_ERROR, "status":status}
            else:
                head_tosend = {"type": RESPONSE_SEARCHMODEL, "status":'', "models": models}

        # elif REQUEST_GETFEATURE == head_recved["type"]:
        #     # 解析模型数据
        #     points_str = head_recved["data"] # string类型
        #     # xyz = struct.unpack(">%df" % len(points_str)/4, points_str)
        #     points = [list(map(float, item.strip().split(','))) for item in points_str.strip().split(' ')]
        #     # # 加载网络模型
        #     # feature_net:PointNetSiamese = load_model(MODE_NET_PATH).cuda()
        #     # 提取特征
        #     feature:torch.Tensor = feature_net.feature(torch.tensor(points).unsqueeze(dim=0).cuda()).squeeze().cpu()
        #     # 返回结果
        #     head_tosend = {"type": RESPONSE_GETFEATURE, "status": '', "feature": feature.tolist()}

        elif REQUEST_UPLOADMODEL == head_recved["type"]:
            # 获取模型数据
            block_size = head_recved['block_size']
            assetType = head_recved['assetType']
            titles = head_recved['titles']
            print(titles)
            if(0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status":"没有数据！"}
            else: # 提取特征
                if (ASSET_MODEL == assetType):
                    head_tosend = import_model(head_recved=head_recved, db=self.db, feature_net=self.fnet)
                elif (ASSET_BVH == assetType):
                    head_tosend = import_bvh(head_recved=head_recved, db=self.db)
                elif (ASSET_EFFECT == assetType):
                    head_tosend = import_effect(head_recved=head_recved, db=self.db)
                # 返回结果
                
        elif REQUEST_UPLOADMODEL_SIMPLE == head_recved["type"]:
            # 获取模型数据
            block_size = head_recved['block_size']
            titles = head_recved['titles']
            print(titles)
            if(0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status":"没有数据！"}
            head_tosend = import_model_simple(head_recved=head_recved, db=self.db, feature_net=self.fnet)
            
        elif REQUEST_SEARCHLABEL == head_recved["type"]:
            block_size = head_recved['block_size']
            category = getModelCategory(head_recved['category'])
            if (0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
            else:
                # data = struct.unpack('%df' % (l // 4), body_recved[offset:offset + l])
                # label = body_recved.decode()
                label = head_recved['info']
                with open('config.yml', 'r') as conf_file:
                    ymlConf = yaml.load(conf_file, Loader= yaml.FullLoader)
                    ipfs_url = ymlConf['ipfs']['url']
                tb_model = TBModel(database=self.db, feature_net=self.fnet, ipfs_url=ipfs_url)
                status, models = tb_model.search_by_label(label, category=category)
                # print(models)
                # 返回结果
                if(None != status):
                    head_tosend = {"type": RESPONSE_ERROR, "status":status}
                else:
                    head_tosend = {"type": RESPONSE_SEARCHLABEL, "status":'', "models": models}

        elif REQUEST_SEARCHTYPE == head_recved["type"]:
            block_size = head_recved['block_size']
            category = getModelCategory(head_recved['category'])
            if (0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
            else:
                # data = struct.unpack('%df' % (l // 4), body_recved[offset:offset + l])
                # type = body_recved.decode()
                type = head_recved['info']
                with open('config.yml', 'r') as conf_file:
                    ymlConf = yaml.load(conf_file, Loader= yaml.FullLoader)
                    ipfs_url = ymlConf['ipfs']['url']
                tb_model = TBModel(database=self.db, feature_net=self.fnet, ipfs_url=ipfs_url)
                status, models = tb_model.search_by_type(type, category=category)
                # print(models)
                # 返回结果
                if(None != status):
                    head_tosend = {"type": RESPONSE_ERROR, "status":status}
                else:
                    head_tosend = {"type": RESPONSE_SEARCHTYPE, "status":'', "models": models}

        elif REQUEST_PULLTYPE   == head_recved["type"]:
            head_tosend = pullType(head_recved=head_recved, db=self.db)
        elif REQUEST_PULLTAGS   == head_recved["type"]:
            head_tosend = pullTags(head_recved=head_recved, db=self.db)
        elif REQUEST_PUSHTYPE   == head_recved["type"]:
            head_tosend = pushType(head_recved=head_recved, db=self.db)
        elif REQUEST_PUSHTAGS   == head_recved["type"]:
            head_tosend = pushTags(head_recved=head_recved, db=self.db)
        elif REQUEST_SHOWTABLE == head_recved["type"]:
            block_size = head_recved['block_size']
            if (0 == len(block_size)):
                head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
            else:
                assetType = head_recved['assetType']
                label = body_recved.decode()
                if (ASSET_MODEL == assetType):
                    tb_name = 'tb_model'
                if (ASSET_BVH == assetType):
                    tb_name = 'tb_bvh'
                if (ASSET_EFFECT == assetType):
                    tb_name = 'tb_effect'
                status, headers = self.db.sqlexe(f'PRAGMA table_info("{tb_name}");')
                if status!=None:
                    head_tosend = {"type": RESPONSE_ERROR, "status": status}
                titles = [h[1] for h in headers]
                status, rows = self.db.sqlexe(f'select * from "{tb_name}"')
                rows = [[str(val) for val in row] for row in rows]
                models = {"titles": titles,
                          "values": rows}
                head_tosend = {"type": RESPONSE_SHOWTABLE, "status":'', "models": models}
        elif REQUEST_INDEXPULL == head_recved["type"]:
            head_tosend = pullIndex(head_recved=head_recved, db=self.db)
        elif REQUEST_INDEXPUSH == head_recved["type"]:
            head_tosend = pushIndex(head_recved=head_recved, db=self.db)
        elif REQUEST_VERSIONCREATE == head_recved["type"]:
            head_tosend = versionCreate(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONPULL == head_recved["type"]:
            head_tosend = versionPull(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONADD == head_recved["type"]:
            head_tosend = versionAdd(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONSYNC == head_recved["type"]:
            head_tosend = versionSync(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONQUERY == head_recved["type"]:
            head_tosend = versionQuery(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONDELETE == head_recved["type"]:
            head_tosend = versionDelete(head_recved=head_recved, db=self.dbv)
        elif REQUEST_VERSIONROLLBACK == head_recved["type"]:
            head_tosend = versionRollback(head_recved=head_recved, db=self.dbv)
        elif REQUEST_SC == head_recved["type"]:
            head_tosend = standardClassification(head_recved=head_recved, db=self.db)
        elif REQUEST_LOGIN == head_recved["type"]:
            head_tosend = login(head_recved=head_recved, db=self.db)
        elif REQUEST_SIGNUP == head_recved["type"]:
            head_tosend = signup(head_recved=head_recved, db=self.db)
        elif REQUEST_DOWNLOAD == head_recved["type"]:
            head_tosend = download(head_recved=head_recved, db=self.db)
        elif REQUEST_SEARCH_TEXT == head_recved["type"]:
            head_tosend = search_text(head_recved=head_recved, db=self.db)
        elif REQUEST_SEARCH_FILE == head_recved["type"]:
            head_tosend = search_file(head_recved=head_recved, fnet=self.fnet, db=self.db)
        elif REQUEST_INTERFACE_INIT == head_recved["type"]:
            with open('config.yml', 'r') as conf_file:
                ymlConf = yaml.load(conf_file, Loader= yaml.FullLoader)
                internal_interface = ymlConf["internal-interface"]
            head_tosend = {
                "type": RESPONSE_INTERFACE_INIT, "status": "",
                "host": internal_interface['host'],
                "port": internal_interface['port'],
                "resource_by_text": internal_interface['resource_by_text'],
                "resource_by_file": internal_interface['resource_by_file'],
                "listeningPath": internal_interface['listeningPath'],
                "cacheFilePath": os.path.abspath(ymlConf['filepath']['ipfs-cache'])
            }
        elif REQUEST_DAMAGE_UPLOAD == head_recved["type"]:
            head_tosend = damage_upload(head_recved=head_recved, fnet=self.fnet, db=self.db)
            logger.info("RESPONSE_DAMAGE_UPLOAD Send: %s" % head_tosend)
        else:
            head_tosend = {"type": RESPONSE_ERROR, "status":'Command not supported.',}
        logger.info("Response Type: %s, Status: '%s'" % (protocalToStr(head_tosend["type"]), head_tosend["status"]))
        sendModel(self.s, head_tosend, body_tosend)
        self.s.close()

def parse_args():
    parser = argparse.ArgumentParser('server')
    parser.add_argument('--host', type=str, default='localhost')
    parser.add_argument("--user", type=str, default='root')
    parser.add_argument("--passwd", type=str, default='123456')
    parser.add_argument("--database", type=str, default='test3d')

    return parser.parse_args()

if __name__ == '__main__':
    # 解析参数
    args = parse_args()
    
    # 解析配置文件config.yml
    with open('config.yml', 'r') as conf_file:
        ymlConf = yaml.load(conf_file, Loader= yaml.FullLoader)
        host = ymlConf["server"]['host']  # 获取本地主机名
        port = ymlConf["server"]['port']  # 设置端口
        db_model_name = ymlConf['db']['model']
        db_version_name = ymlConf['db']['version']
        point_net_name = ymlConf['point-net']['name']
        point_net_basename = ymlConf['point-net']['basename']
        filepath_cache = ymlConf['filepath']['ipfs-cache']
        filepath_version = ymlConf['filepath']['version-folder']
    
    # 加载网络模型
    feature_net = loadModel(point_net_name, point_net_basename)
    # 连接数据库
    database = DBMysql(name = db_model_name)
    database_version = DBMysql(name = db_version_name)
    s = socket.socket()         # 创建 socket 对象
    
    s.bind((host, port))        # 绑定端口

    s.listen(5)                 # 等待客户端连接

    if os.path.exists(filepath_cache) is False:
        os.mkdir(filepath_cache)
    if os.path.exists(filepath_version) is False:
        os.mkdir(filepath_version)

    while True:
        logger.info('server listening %s:%d' % (host, port))
        c, addr = s.accept()     # 建立客户端连接
        logger.info('连接地址：%s', addr)
        # 创建新线程
        new_thread = ThreadProcess(c, feature_net, database, database_version)
        # 开启新线程
        new_thread.start()
        # new_thread.join()
        # c.close()